/**
 * Created on 13 avr. 08 by Sebastian Audet
 */
package gui2Dengine;

import grid.GridOutOfBounds;
import grid.HexGrid;
import grid.Location;
import grid.Node;

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.RadialGradientPaint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.Area;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeSet;

import javax.imageio.ImageIO;
import javax.swing.JFrame;

import tile.MyTileSet;
import tile.People;
import tile.Tile;
import tile.TintTiles;

/**
 * @author Sebastian Audet
 * 
 */
public class HexGridPanel extends GridPanel {

    HexDisplayMap mapper;
    private boolean drawgridlines = false;
    private Graphics2D graphics;
    private static double SIXTY_DEG = Math.PI / 3;
    private static double CTRIG_FUNC = Math.cos(SIXTY_DEG);
    private static double STRIG_FUNC = Math.sin(SIXTY_DEG);

    public void setGridLines(boolean b) {
	drawgridlines = b;
    }

    public HexGridPanel(HexGrid grid, float strokewidth, int width, int height,
	    int xunits, int yunits, int xoffset, int yoffset) {
	super(grid, strokewidth, width, height, xunits, yunits, xoffset,
		yoffset);

	double xlength = width / xunits;
	double ylength = height / yunits;
	double dxlength = (xlength / (2 + (2 * CTRIG_FUNC)));
	dxlength = dxlength - (dxlength * CTRIG_FUNC / xunits);
	double dylength = (int) (ylength / STRIG_FUNC);
	dylength = dylength - (dylength * STRIG_FUNC / yunits);
	// Set sidelength to the smaller value
	if (dxlength < dylength) {
	    sidelength = dxlength;
	} else {
	    sidelength = dylength;
	}

	mapper = new HexDisplayMap(grid, sidelength, strokewidth);
	mapper.generateShapeData(xoffset, yoffset, xunits, yunits);
    }

    /**
     * long serialVersionUID
     */
    private static final long serialVersionUID = 6262488073081959677L;

    public void renderGrid(Graphics2D g2) {

	g2.setBackground(Color.black);

	g2.setStroke(new BasicStroke(strokewidth, BasicStroke.CAP_ROUND,
		BasicStroke.JOIN_BEVEL, 1.0f, null, 0));
	TreeSet<Node> nodes = (TreeSet<Node>) grid.getGridNodesCollection();
	do {
	    Iterator<Node> itr = nodes.iterator();
	    while (itr.hasNext()) {
		renderGridNode(itr.next(), g2);
	    }
	} while (false);
	if (drawgridlines) {
	    g2.setPaint(Color.BLACK);
	    for (Shape path : mapper.shapes) {
		g2.draw(path);
	    }
	}
    }

    public void renderGridNode(Node node, Graphics2D g2) {
	Hexagon hex = mapper.pointmap.get(node.getLocation());
	if (hex != null)// If this node was mapped
	{
	    Shape shape = hex.getHexagon();
	    if(new Area(shape).intersects(getVisibleRect())) {
	    	visibletiles.add(node.getLocation());
	    }
	    Point2D.Double center = hex.getCenter();
	    double radius = hex.getRadiusDistance();
	    if (node.isActive()) {
		float[] fractions = { 0.1f, 0.9f };
		Color[] colors = { Color.white, Color.gray };
		g2.setPaint(new RadialGradientPaint((float) center.x,
			(float) center.y, (float) (radius * 1.1), fractions,
			colors));
		g2.fill(shape);
		TreeSet<Tile> tiles = node.returnTiles();
		if (tiles != null) {
		    Iterator<Tile> itr = tiles.iterator();
		    ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
		    Location loc = node.getLocation();
		    while (itr.hasNext()) {
			Tile t = itr.next();
			try {
			    images.add(ImageIO.read(t.getResource()));
			} catch (IOException e) {
			    System.out.println("Failed to load symbol " + t.getResource() + " at " + loc.toString());
			} catch (IllegalArgumentException e) {
			    System.out.println("Invalid symbol: "+t.getString()+" at "+loc.toString());
			}
		    }
		    double imagewidth = hex.getBisectorDistance();
		    int iimagewidth = new Double(imagewidth).intValue();
		    Shape regclip = g2.getClip();
		    g2.setClip(shape);
		    int x = new Double(center.x - imagewidth / 2).intValue();
		    int y = new Double(center.y - imagewidth / 2).intValue();
		    for (BufferedImage i : images) {
			g2.drawImage(i.getScaledInstance(iimagewidth,
				iimagewidth, BufferedImage.SCALE_FAST), x, y,
				null);
		    }
		    g2.setClip(regclip);
		}
	    } else {
		float[] fractions = { 0.1f, 0.9f };
		Color[] colors = { new Color(30, 30, 30), Color.black };
		g2.setPaint(new RadialGradientPaint((float) center.x,
			(float) center.y, (float) radius, fractions, colors));
		g2.fill(shape);
	    }
	}
    }

    public void initmethod(Graphics2D g2) {
	RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING,
		RenderingHints.VALUE_ANTIALIAS_ON);
	g2.addRenderingHints(rh);
	graphics = (Graphics2D) g2.create();
	renderGrid(g2);
    }

    public void painter(Graphics g) {
	initmethod((Graphics2D) g);
    }

    public static void main(String[] args) {
	/*if (args.length == 0) {
	    pl("Choose windowed or fullscreen mode");
	    Scanner in = new Scanner(System.in);
	    boolean done = false;
	    do {
		if (in.next().equalsIgnoreCase("w")) {
		    windowed();
		    done = true;
		} else if (in.next().equalsIgnoreCase("f")) {
		    fullscreen();
		    done = true;
		} else {

		}
	    } while (!done);
	} else if (args.length == 1) {
	    if (args[0].equalsIgnoreCase("w")) {
		windowed();
	    } else if (args[0].equalsIgnoreCase("f")) {
		fullscreen();
	    }
	}*/windowed();
    }

    private static void windowed() {

	JFrame t = new JFrame();
	t.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	t.setSize(600, 600);
	t.setLayout(new BorderLayout());
	t.setUndecorated(false);
	HexGridPanel p = null;
	try {
	    HexGrid grid = new HexGrid(30, 60);
	    grid.readInActivation("maps/Activation");
	    grid.readInGrid("maps/GridFile", new MyTileSet());
	    grid.readInGrid("maps/GridFile2", new People());
	    grid.readInGrid("maps/Tintmap", new TintTiles());
	    p = new HexGridPanel(grid, 2, 600, 600, 15, 50, 0, 0);
		t.setContentPane(p);
	} catch (Exception e) {
	    e.printStackTrace();
	} catch (GridOutOfBounds e) {
	    e.printStackTrace();
	}
	t.setVisible(true);

	/*try {
	    Tile hero = p.grid.getNode(new Location(4, 5)).returnLevel(4);
	    Thread.sleep(2000);
	    p.moveTile(hero, p.grid.getNode(new Location(4, 5)), p.grid
		    .getNode(new Location(5, 5)));
	    Thread.sleep(2000);
	    p.moveTile(hero, p.grid.getNode(new Location(5, 5)), p.grid
		    .getNode(new Location(7, 5)));
	} catch (GridOutOfBounds e) {
	    pl("Failed");
	    e.printStackTrace();
	} catch (InterruptedException e) {
	    pl("Failed");
	    e.printStackTrace();
	}*/
    }

    private static void fullscreen() {
	GraphicsEnvironment env = GraphicsEnvironment
		.getLocalGraphicsEnvironment();
	GraphicsDevice device = env.getDefaultScreenDevice();
	if (device.isFullScreenSupported()) {
	    JFrame t = new JFrame();
	    t.setUndecorated(true);
	    t.setIgnoreRepaint(true);
	    device.setFullScreenWindow(t);
	    t.createBufferStrategy(10);
	    HexGridPanel p = null;
	    try {
		HexGrid grid = new HexGrid(30, 60);
		// grid.readInActivation(new File("maps/Activation"));
		grid.readInGrid(new File("maps/GridFile"), new MyTileSet());
		grid.readInGrid(new File("maps/GridFile2"), new People());
		grid.readInGrid(new File("maps/Tintmap"), new TintTiles());
		p = new HexGridPanel(grid, 2, 1280, 800, 15, 50, 0, 0);
	    } catch (Exception e) {
		e.printStackTrace();
	    } catch (GridOutOfBounds e) {
		e.printStackTrace();
	    }
	    t.setContentPane(p);
	    t.setVisible(true);

	    try {
		Tile hero = p.grid.getNode(new Location(4, 5)).returnLevel(4);
		Thread.sleep(2000);
		p.moveTile(hero, p.grid.getNode(new Location(4, 5)), p.grid
			.getNode(new Location(5, 5)));
		Thread.sleep(2000);
		p.moveTile(hero, p.grid.getNode(new Location(5, 5)), p.grid
			.getNode(new Location(7, 5)));
	    } catch (GridOutOfBounds e) {
		pl("Failed");
		e.printStackTrace();
	    } catch (InterruptedException e) {
		pl("Failed");
		e.printStackTrace();
	    }
	}
    }

    @Override
    public boolean moveTile(Tile tile, Node originatingNode, Node receivingNode) {
	return moveTile(tile, originatingNode.getLocation(), receivingNode
		.getLocation());
    }

    @Override
	public boolean move(Tile tile, Node node, int dir) {
	Node[] anodes = grid.getEmptyNeighborNodes(node);
	if (dir > anodes.length || dir == 0) {
	    return false;
	} else {
	    return moveTile(tile, node, anodes[dir - 1]);
	}
	}

	@Override
    public boolean moveTile(Tile tile, Location oloc, Location rloc) {
	boolean retval = grid.moveTile(tile, oloc, rloc);
	try {
	    renderGridNode(grid.getNode(oloc), graphics);
	    renderGridNode(grid.getNode(rloc), graphics);
	} catch (GridOutOfBounds e) {
	    e.printStackTrace();
	}
	return retval;
    }

    @Override
    public boolean addTile(Tile tile, Node node) {
	return addTile(tile, node.getLocation());
    }

    @Override
    public boolean addTile(Tile tile, Location loc) {
	boolean retval = grid.addTile(tile, loc.x, loc.y);
	try {
	    renderGridNode(grid.getNode(loc), graphics);
	} catch (GridOutOfBounds e) {
	    e.printStackTrace();
	}
	return retval;
    }

    @Override
    public void eraseNode(Node node) {
	node.clearNode();
	renderGridNode(node, graphics);
    }

    @Override
    public void eraseNode(Location loc) {
	try {
	    eraseNode(grid.getNode(loc));
	} catch (GridOutOfBounds e) {
	    e.printStackTrace();
	}
    }

    @Override
    public boolean removeTile(Tile tile, Location loc) {
	boolean retval = grid.removeTile(tile, loc.x, loc.y);
	try {
	    renderGridNode(grid.getNode(loc), graphics);
	} catch (GridOutOfBounds e) {
	    e.printStackTrace();
	}
	return retval;
    }

    @Override
    public boolean removeTile(Tile tile, Node node) {
	return removeTile(tile, node.getLocation());
    }

    @Override
	public Node getNode(Point point) {
		if(this.contains(point)) {
			Iterator<Location> itr = this.visibletiles.iterator();
			while(itr.hasNext()) {
				Location temp = itr.next();
				Shape shape = this.mapper.shapemap.get(temp);
				if(shape.contains(point)) {
					try {
						return grid.getNode(temp);
					} catch (GridOutOfBounds e) {
						e.printStackTrace();
					}
				}
			}
			return null;
		} else {
		return null; }
	}

}
